package com.example.demo.controller;

import com.alibaba.fastjson.JSONObject;
import com.example.demo.annotate.CreateSign;
import com.example.demo.common.BCrypt;
import com.example.demo.common.Convert;
import com.example.demo.common.Result;
import com.example.demo.common.Valid;
import com.example.demo.common.constant.RuleCode;
import com.example.demo.entity.User;
import com.example.demo.entity.config.CustomeParam;
import com.example.demo.mapper.UserMapper;
import com.example.demo.verify.ParamValid;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/jwt")
@Api(tags = "9、jwt加密管理")
@ApiSupport(author = "罗海林", order = 9)
@Validated
@RequiredArgsConstructor
public class JwtController {

    private final RedisTemplate<String, String> redis;
    private final UserMapper userMapper;
    private final CustomeParam param;

    @PostMapping("/getJwtString")
    @ApiOperation(value = "获取加密字符串", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 1)
    @CreateSign
    public Result getJwtString() {
        JSONObject head = new JSONObject();
        head.put("alg", SignatureAlgorithm.HS256.getValue());
        head.put("typ", "JWT");

        JSONObject body = new JSONObject();
        body.put("userid", "C8C4D56E14F62C79FB1E7274901912A1");
        body.put("username", "aaa");
        body.put("password", "U01MUXI%3D");
        body.put("name", "罗海林");
        body.put("time", "1234567890");
        body.put("random", "654321");

        String jwt = Jwts.builder()
                .setHeader(head)
                .setClaims(body)
                .setId("jwt001")
                .signWith(SignatureAlgorithm.HS256, param.getJwtPassword())
                .compact();

        Result res = new Result();
        res.ok();
        res.put("token", jwt);
        return res;
    }

    @PostMapping("/analyseJwtString")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "token", value = "token字符串")
    })
    @ApiOperation(value = "解析加密字符串", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 2)
    public Result analyseJwtString(@ParamValid(name = "token字符串", rule = RuleCode.noEmpty) @RequestParam String token,
                                   @ParamValid(name = "密钥", rule = RuleCode.noEmpty) @RequestParam String password) {
        Jwt result = Jwts.parser().setSigningKey(password).parse(token);
        Object body = result.getBody();
        Header head = result.getHeader();

        Result res = new Result();
        res.ok();
        res.put("body", body);
        res.put("head", head);
        return res;
    }

    @PostMapping("/getUserList")
    @ApiOperation(value = "获取启用的用户列表", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 3)
    public Result getUserList() {
        MPJLambdaWrapper<User> query = new MPJLambdaWrapper<>();
        query.select(User::getUserId, User::getUsername, User::getPassword);
        query.eq(User::getIsUse, "1");

        List<Map<String, Object>> list = userMapper.selectMaps(query);

        for (Map<String, Object> info : list) {
            String key = "token_" + info.get("userid").toString();
            redis.opsForValue().set(key, info.toString(), 1200, TimeUnit.SECONDS);
        }

        Result res = new Result();
        res.ok();
        res.put("list", list);
        return res;
    }

    @PostMapping("/getUserInfo")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "userid", value = "用户ID"),
            @ApiImplicitParam(name = "username", value = "用户名")
    })
    @ApiOperation(value = "获取redis的用户信息", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 4)
    public Result getUserInfo(@ParamValid(name = "用户ID", rule = RuleCode.noEmpty) @RequestParam String userid,
                              @ParamValid(name = "用户名", rule = RuleCode.noEmpty) @RequestParam String username) {
        Result res = new Result();

        String value = redis.opsForValue().get("token_" + userid);
        if (Valid.isEmpty(value)) {
            res.setValidError("redis里没有相应的key值");
            return res;
        }

        JSONObject js = Convert.mapStringToJson(value);
        if (!username.equals(js.getString("username"))) {
            res.setValidError("用户名不正确");
            return res;
        }

        res.ok();
        res.put("info", js);
        return res;
    }

    @PostMapping("/getBCryptPassword")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "password", value = "密码")
    })
    @ApiOperation(value = "获取BCrypt密码", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 5)
    public Result getBCryptPassword(@ParamValid(name = "密码", rule = RuleCode.noEmpty) @RequestParam String password) {
        String gensalt = BCrypt.gensalt();
        String result = BCrypt.hashpw(password, gensalt);

        Result res = new Result();
        res.ok();
        res.put("password", result);
        res.put("gensalt", gensalt);
        return res;
    }

    @PostMapping("/checkBCryptPassword")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "password1", value = "加密后"),
            @ApiImplicitParam(name = "password2", value = "原密码")
    })
    @ApiOperation(value = "校验BCrypt密码", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 6)
    public Result checkBCryptPassword(@ParamValid(name = "加密后", rule = RuleCode.noEmpty) @RequestParam String password1,
                                      @ParamValid(name = "原密码", rule = RuleCode.noEmpty) @RequestParam String password2) {
        boolean checkpw = BCrypt.checkpw(password2, password1);

        Result res = new Result();
        res.ok();
        res.put("result", checkpw);
        return res;
    }

}
